use ggez::{glam::Vec2,Context, graphics, mint::Point2};
use rand;
use rand_distr::{Distribution, Normal};
use noise::{Perlin, NoiseFn};

use crate::mover::Mover;
use crate::physics::Physics;
use crate::visual::Visual;
pub struct NervousFly {
    physics : Physics,
    visual: Visual,
    perlin: Perlin,
    tx: f64,
    ty: f64
}

impl NervousFly {
    pub fn new(location: Vec2) -> NervousFly {
        let mut rng = rand::thread_rng();
        let normal = Normal::new(0.0, 1.0).unwrap();
        let factor = normal.sample(&mut rng) as f32;
        let velocity = Vec2{x: 0.0, y: 0.0};
        let acceleration = Vec2 { x: 0.0, y: 0.0 };
        let topspeed = factor*20.0+70.0;
        let physics = Physics{location, velocity, topspeed, acceleration};
        let color = graphics::Color::new(1.0-(factor*0.2+0.75), factor*0.2+0.75, 0.5, 1.0);
        let size = 10.0 - (factor*0.6 + 8.0);
        let visual = Visual{color, size};
        NervousFly {physics, visual, perlin: Perlin::new((factor*10.0+20.0) as u32), tx: 0.0, ty: 0.0}
    }

    pub fn limit(&mut self) -> () {
        if self.physics.velocity.length() > self.physics.topspeed {self.physics.velocity = self.physics.topspeed*self.physics.velocity.normalize()}
    }
}

impl Mover for NervousFly {
    fn update(&mut self, ctx: &mut Context) -> () {
        let target_point = ctx.mouse.position();
        let target = Vec2{x: target_point.x, y: target_point.y};
        let distance = (target-self.physics.location).length();


        self.physics.acceleration = (target - self.physics.location).normalize()*200.0/distance;
     
        let x = 2.0*self.perlin.get([self.tx, self.ty]) as f32;
        let y = 2.0*self.perlin.get([self.tx+1.2, self.ty+2.1]) as f32;
        self.physics.velocity += Vec2{x,y};
        self.tx += 0.04;
        self.ty += 0.006;
        self.physics.velocity += self.physics.acceleration;
        self.physics.velocity -= self.physics.velocity*self.physics.velocity*self.physics.velocity*0.0001;
        self.limit();
        
        self.physics.location += self.physics.velocity;
        self.physics.check_edges(ctx);
    }

    fn display(&self, ctx: &mut Context, canvas : &mut graphics::Canvas) -> () {
        let circle = graphics::Mesh::new_circle(
            ctx,
            graphics::DrawMode::fill(),
            Point2{x: self.physics.location.x, y: self.physics.location.y},
            self.visual.size,
            0.1,
            self.visual.color,
        ).unwrap();
        canvas.draw(&circle, graphics::DrawParam::default());
        let circle = graphics::Mesh::new_circle(
            ctx,
            graphics::DrawMode::stroke(2.0),
            Point2{x: self.physics.location.x, y: self.physics.location.y},
            self.visual.size,
            0.1,
            graphics::Color::BLACK,
        ).unwrap();
        canvas.draw(&circle, graphics::DrawParam::default());
    }
}